package com.yumo.kangchenjunga.company;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.ExampleMatcher.StringMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.yumo.kangchenjunga.activity.ActivityService;
import com.yumo.kangchenjunga.common.ExistsAssociationException;
import com.yumo.kangchenjunga.common.Status;
import com.yumo.kangchenjunga.exception.InvalidArgumentException;
import com.yumo.kangchenjunga.sms.SmsVerificationCodeService;
import com.yumo.kangchenjunga.user.NoSuchUserException;
import com.yumo.kangchenjunga.user.UserService;
import com.yumo.kangchenjunga.user.UserVo;

@Service
public class CompanyService {

	public static final String ADMIN_NAME = "admin";
	public static final String ADMIN_PRIVILEGE = "company_admin";

	@Autowired
	private CompanyRepository repository;
	@Autowired
	private StoreRepository storeRepository;
	@Autowired
	private CompanyCategoryRepository categoryRepository;
	@Autowired
	private ActivityService activityService;
	@Autowired
	private UserService userService;
	@Autowired
	private SmsVerificationCodeService smsVerificationCodeService;

	public CompanyVo queryById(int id) throws NoSuchCompanyException {
		return repository.findById(id).map(CompanyVo::new).orElseThrow(() -> createNoSuchCompanyException(id));
	}

	public List<CompanyVo> list() {
		return repository.findByIdGreaterThan(1).stream().map(CompanyVo::new).collect(Collectors.toList());
	}

	public Page<CompanyVo> page(Pageable pageable) {
		return repository.findByIdGreaterThan(1, pageable).map(CompanyVo::new);
	}

	@Transactional
	public CompanyVo create(CompanyVo company) throws NoSuchCompanyException {
		var entity = CompanyEntity.create(repository, new CompanyEntity(company));
		var user = new UserVo();

		user.companyId = entity.getId();
		user.password = "000000";
		user.privilege = ADMIN_PRIVILEGE;
		user.username = ADMIN_NAME;

		userService.createCompanyUser(user);

		return new CompanyVo(entity);
	}

	public void delete(int id) throws ExistsAssociationException {
		if (notExistsAssociation(id) && id > 1) {
			repository.deleteById(id);
		} else {
			throw new ExistsAssociationException();
		}
	}

	private boolean notExistsAssociation(int id) {
		return !activityService.existsByCompanyId(id);
	}

	public CompanyVo modify(int id, CompanyVo company) throws NoSuchCompanyException {
		Optional<CompanyEntity> row = repository.findById(id);

		if (row.isPresent()) {
			return new CompanyVo(repository.save(new CompanyEntity(company)));
		}

		throw createNoSuchCompanyException(id);
	}

	public Page<CompanyVo> query(CompanyVo company, Pageable pageable) {
		ExampleMatcher matcher = ExampleMatcher.matchingAll().withIgnoreCase().withIgnorePaths("id")
				.withStringMatcher(StringMatcher.CONTAINING);
		Example<CompanyEntity> example = Example.of(new CompanyEntity(company), matcher);

		return repository.findAll(example, pageable).map(CompanyVo::new);
	}

	public StoreVo createStore(StoreVo store) {
		return new StoreVo(StoreEntity.create(storeRepository, new StoreEntity(store)));
	}

	public StoreVo queryStoreById(int storeId) throws NoSuchStoreException {
		return storeRepository.findById(storeId).map(StoreVo::new).orElseThrow(NoSuchStoreException::new);
	}

	public List<StoreVo> queryStoreByCompanyId(int companyId) {
		return storeRepository.findStoreByCompanyIdOrderByName(companyId).stream().map(StoreVo::new)
				.collect(Collectors.toList());
	}

	public Page<StoreVo> pageStore(Pageable pageable) {
		return storeRepository.findAll(pageable).map(StoreVo::new);
	}

	public void deleteStore(int storeId) {
		storeRepository.deleteById(storeId);
	}

	public StoreVo modifyStore(int storeId, StoreVo store) throws NoSuchStoreException {
		Optional<StoreEntity> row = storeRepository.findById(storeId);

		if (row.isPresent()) {
			return new StoreVo(storeRepository.save(new StoreEntity(store)));
		}

		throw new NoSuchStoreException();
	}

	public Page<StoreVo> queryStores(StoreVo store, Pageable pageable) {
		ExampleMatcher matcher = ExampleMatcher.matchingAll().withIgnoreCase().withIgnorePaths("id")
				.withStringMatcher(StringMatcher.CONTAINING);
		Example<StoreEntity> example = Example.of(new StoreEntity(store), matcher);

		return storeRepository.findAll(example, pageable).map(StoreVo::new);
	}

	public CompanyEntity findById(int companyId) throws NoSuchCompanyException {
		return repository.findById(companyId).orElseThrow(() -> createNoSuchCompanyException(companyId));
	}

	private NoSuchCompanyException createNoSuchCompanyException(int companyId) {
		return new NoSuchCompanyException(String.format("companyId: %d.", companyId));
	}

	public void changeAdminPassword(int companyId, int userId, String verificationCode, String newPassword)
			throws NoSuchUserException {
		String key = String.format("%d:%d", companyId, userId);

		if (smsVerificationCodeService.verify(key, null, verificationCode)) {
			userService.changeCompanyAdminPassword(companyId, newPassword);
		} else {
			throw new IllegalArgumentException("验证码不正确！");
		}
	}

	public CompanyCategoryEntity createCategory(CompanyCategoryEntity entity) {
		return CompanyCategoryEntity.create(categoryRepository, entity);
	}

	public CompanyCategoryEntity deleteCategory(int categoryId) {
		var entity = categoryRepository.findById(categoryId).orElseThrow();
		var companies = repository.findByCategory(entity);

		if (companies.isEmpty()) {
			categoryRepository.delete(entity);
		} else {
			throw new InvalidArgumentException("还有企业在使用该类型，无法删除。");
		}

		return entity;
	}

	public CompanyCategoryVo queryCategoryById(int id) {
		return categoryRepository.findById(id).map(CompanyCategoryVo::new).orElseThrow();
	}

	public List<CompanyCategoryEntity> listCategory() {
		return categoryRepository.findByStatus(Status.VALID);
	}

	public Page<CompanyCategoryEntity> pageCategory(Pageable pageable) {
		return categoryRepository.findByStatus(Status.VALID, pageable);
	}

	public void requireCompanyExists(int companyId) throws NoSuchCompanyException {
		if (!repository.existsById(companyId)) {
			throw new NoSuchCompanyException(String.format("companyId: %d.", companyId));
		}
	}

	public String sendVerificationCode(int companyId, int userId) throws NoSuchCompanyException {
		String key = String.format("%d:%d", companyId, userId);
		var company = repository.findById(companyId).orElseThrow(() -> createNoSuchCompanyException(companyId));

		return smsVerificationCodeService.generate(key, company.phone);
	}

}
